/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#pragma once

#include <baja/common.hpp>
#include <baja/math.hpp>
#include <journal/views/types.h>
#include <baja/app/input.hpp>
#include <journal/models/interfaces.h>
#include <journal/views/animation.hpp>
#include <baja/stream/storage_file_interface.h>

namespace journal { namespace view {

namespace b = baja;
namespace a = baja::app;
namespace g = baja::graphics;
namespace gs = baja::graphics::scenes;
namespace m = baja::math;

// fwd decls
struct iphoto_observer;
struct ipage_observer;
struct iink_surface_observer;
struct inotebook_observer;
struct icamera_observer;
struct icamera;

// TRACK: 
// there's a few interfaces in here that aren't specific to views and could/should be moved to the app layer 
// (memory blob, shared image, ui observer)

struct icamera_service : public b::ibase
{
    BAJA_INTERFACE(icamera_service, "D7551FA8-755A-449A-99CE-8669174F5EEB");

    virtual b::float32 pixelToWorld(const b::float32 pixel) = 0;
    virtual b::float32 worldToPixel(const b::float32 world) = 0;
    virtual const std::shared_ptr<icamera> camera() = 0;
};

struct icommand_observer : public b::ibase
{
    BAJA_INTERFACE_NOTAG(icommand_observer);

    virtual void onCommand(const command_id id, void* context = nullptr) = 0;
};

struct imemory_blob : public b::ibase
{
    BAJA_INTERFACE_NOTAG(imemory_blob);

    virtual const b::memory* bytes() = 0;
    virtual size_t count() = 0;
};

struct ishared_image : public b::ibase
{
    BAJA_INTERFACE_NOTAG(ishared_image);

    virtual std::shared_ptr<imemory_blob> thumbnail() = 0;
    virtual std::shared_ptr<imemory_blob> image() = 0;
    virtual std::wstring title() = 0;
};

struct ishare_source : public b::ibase
{
    BAJA_INTERFACE_NOTAG(ishare_source);

    virtual void onShareImageAsync(copy_info& info, std::function<void(const std::shared_ptr<view::ishared_image>&)> f) = 0;
    virtual void onCopyImageAsync(copy_info& info, std::function<void(const std::shared_ptr<view::ishared_image>&)> f) = 0;
    virtual void onPasteImageAsync(const std::shared_ptr<view::ishared_image>& image, std::function<void()> f) = 0;
};

struct iphoto_sink: public b::ibase
{
    BAJA_INTERFACE_NOTAG(iphoto_sink);

    virtual void onPhotoFileSelected(const std::shared_ptr<b::storage::istorage_file>& file) = 0;
};

struct icamera : public b::ibase
{
    BAJA_INTERFACE_NOTAG(icamera);

    virtual void apply(const std::shared_ptr<g::iengine>& graphics) = 0;

    virtual const m::matrix44& matrix() = 0;
    virtual const m::vector3d& angles() = 0;
    virtual const m::vector2d& offsetXY() = 0;
    virtual const b::float32 offsetDistance() = 0;
    virtual const m::vector2d& focus() = 0;
    virtual const b::uint64 changeNumber() = 0;
    virtual const b::float32 scale() = 0;
    virtual const m::vector3d& direction() = 0;
    virtual const m::point3d& position() = 0;

    virtual void setScale(const b::float32 scale) = 0;
    virtual void setAngles(const m::vector3d& angles) = 0;
    virtual void setOffsetXY(const m::vector2d& offsetXY) = 0;
    virtual void setOffsetDistance(const b::float32 offsetDistance) = 0;
    virtual void setFocus(const m::vector2d& focus) = 0;

    virtual void refresh(const std::shared_ptr<g::iengine>& graphics) = 0;

    virtual b::int32 addObserver(const std::shared_ptr<icamera_observer>& observer) = 0;
    virtual void removeObserver(const std::shared_ptr<icamera_observer>& observer) = 0;
    virtual void removeObserver(const b::int32 cookie) = 0;
};

struct ideferred_render_object : public b::ibase
{
    BAJA_INTERFACE_NOTAG(ideferred_render_object);

    virtual void onRenderDeferred() = 0;
};

struct icached_render_object : public b::ibase
{
    BAJA_INTERFACE_NOTAG(icached_render_object);

    virtual bool shouldRebuildCache() = 0;
    virtual void setRebuildCache(const bool value) = 0;
    virtual void rebuildRenderData() = 0;
    virtual bool renderToCache() = 0;
    virtual void renderDynamic() = 0;
    virtual bool getDynamicBounds(m::rectangle* bounds) = 0;
};

struct iscene_references : public a::iinput_observer, public a::irender_observer
{
    BAJA_INTERFACE_NOTAG(iscene_references);

    virtual void setVisible(const bool visible) = 0;
    virtual bool visible() = 0;
};

struct iinput_references: public a::irender_observer, public a::iinput_observer
{
    BAJA_INTERFACE_NOTAG(iinput_references);
};

struct ipage_layout : public b::ibase
{
    BAJA_INTERFACE_NOTAG(ipage_layout);

    virtual void setReference(const size_t referencePageNumber, const m::point3d referenceWorld) = 0;
    virtual void constrainOffsetXY(m::point2d& offsetXY) = 0;
    virtual void constrainScale(b::float32& scale) = 0;
    virtual void calculatePageSet(
        const m::vector2d& offsetXY,
        const m::vector2d& focus,
        const page_set& currentPageSet,
        size_t currentPage,
        page_set* pageSet, 
        size_t* pageNumber
        ) = 0;
    virtual m::point3d calculatePagePosition(const size_t pageNumber) = 0;
    virtual m::point2d calculateCameraOffsetXY(const size_t pageNumber) = 0;
    virtual size_t calculatePageNumber(m::point3d& position) = 0;
    virtual b::float32 scale() = 0;
    virtual m::point2d focus() = 0;
    virtual m::point3d angles() = 0;
    virtual b::float32 distance() = 0;
    virtual void calculateScroll(
        const m::vector2d& offsetXY,
        const m::vector2d& focus,
        m::point2d& dimensions,
        m::point2d& position
        ) = 0;
};

struct islotted_page_container : public b::ibase
{
    BAJA_INTERFACE_NOTAG(islotted_page_container);

    virtual std::shared_ptr<gs::iscene_graph> sceneGraph() = 0;
    virtual std::shared_ptr<ipage_observer> pageObserver() = 0;
    virtual size_t checkSlot(const m::point3d& position) = 0;
    virtual void claimSlot(const m::point3d& position, const size_t pageNumber) = 0;
    virtual void freeSlot(const m::point3d& position) = 0;
};

struct inotebook : 
    public a::iinput_observer, 
    public a::iinput_capture,
    public a::irender_observer,
    public a::itick_observer,
    public ishare_source,
    public icommand_observer,
    public iphoto_sink
{
    BAJA_INTERFACE_NOTAG(inotebook);
    
    virtual std::shared_ptr<model::inotebook_model> model() = 0;
    virtual b::int32 addObserver(const std::shared_ptr<inotebook_observer>& observer) = 0;
    virtual void removeObserver(const std::shared_ptr<inotebook_observer>& observer) = 0;
    virtual void removeObserver(const b::int32 cookie) = 0;
    virtual size_t pageNumber() = 0;
    virtual layout_id layoutId() = 0;
    virtual std::shared_ptr<gs::iscene_graph> sceneGraph() = 0;
    virtual bool saveThumbnailsAsync() = 0;
    virtual bool areThumbnailsSaved() = 0;
    virtual void generateTileImageAsync(std::function<void(const bool, const std::wstring&)> f) = 0;
    virtual b::uint32 addNewPage() = 0;
};

struct inotebook_automation
{
    BAJA_INTERFACE_NOTAG(inotebook_automation);

    virtual std::shared_ptr<ipage> pageFromId(const b::uint32& pageId) = 0;
};

struct iphoto : 
    public a::iinput_observer,
    public icached_render_object,
    public icommand_observer
{
    BAJA_INTERFACE_NOTAG(iphoto);

    virtual std::shared_ptr<model::iphoto_model> model() = 0;
    virtual void onSiblingPhotoSelected(const b::Boolean onTopOfSelectedPhoto) = 0;
    virtual void onSiblingPhotoDeselected() = 0;
    virtual void beforeRemove() = 0;
    virtual void setAlpha(const b::float32 alpha) = 0;

    virtual b::int32 addObserver(const std::shared_ptr<iphoto_observer>& observer) = 0;
    virtual void removeObserver(const std::shared_ptr<iphoto_observer>& observer) = 0;
    virtual void removeObserver(const b::int32 cookie) = 0;
};

enum ink_surface_tool
{
    drawing_tool,
    eraser_tool
};

struct ipage : 
    public a::iinput_observer, 
    public a::iinput_capture,
    public a::irender_observer,
    public icached_render_object,
    public ishare_source,
    public icommand_observer
{
    BAJA_INTERFACE_NOTAG(ipage);

    virtual std::shared_ptr<model::ipage_model> model() = 0;
    virtual std::shared_ptr<iink_surface> inkSurfaceView() = 0;
    virtual std::shared_ptr<gs::iscene_node> sceneNode() = 0;
    virtual void setVisible(const bool visible) = 0;
    virtual bool visible() = 0;
    virtual void setPosition(const m::point3d& position) = 0;
    virtual m::point3d position() = 0;
    virtual void setSelected(const bool value) = 0;
    virtual bool selected() = 0;
    virtual void setRotation(const m::point3d& angles) = 0;
    virtual m::point3d rotation() = 0;
    virtual bool pixelHitTest(const m::point2d& point, m::point3d* localPoint) = 0;
    virtual void setLoadLevel(const load_level level) = 0;
    virtual b::float32 alpha() = 0;
    virtual void setAlpha(const b::float32 value) = 0;
    virtual void setAmbientColor(const g::color& color) = 0;
    virtual void setCurling(const bool value) = 0;
    virtual bool curling() = 0;
    virtual std::shared_ptr<imemory_blob> generateImage(const m::point2d* dimensions = nullptr) = 0;
    virtual void addPhotoWithScale(const std::shared_ptr<b::storage::istorage_file>& imageFile) = 0;
    virtual void addPhoto(
        const std::shared_ptr<b::storage::istorage_file>& imageFile,
        const b::float32& x,
        const b::float32& y,
        const b::float32& scale,
        const b::float32& angle) = 0;

    virtual bool saveThumbnailAsync() = 0;
    virtual bool isThumbnailSaved() = 0;
    virtual void renderShadow(b::float32 x, b::float32 y, b::float32 scaleX) = 0;

    virtual b::int32 addObserver(const std::shared_ptr<ipage_observer>& observer) = 0;
    virtual void removeObserver(const std::shared_ptr<ipage_observer>& observer) = 0;
    virtual void removeObserver(const b::int32 cookie) = 0;

    virtual void onSelectInkTool(ink_surface_tool tool) = 0;
    virtual void onRemoveSelectedPhoto() = 0;
};

struct ipage_automation :
    public b::ibase
{
    BAJA_INTERFACE_NOTAG(ipage_automation);

    virtual void generateFractalInkAsync() = 0;
    virtual std::shared_ptr<iink_surface> inkSurface() = 0;
};

struct iink_surface : 
    public a::iinput_observer,
    public icached_render_object,
    public ideferred_render_object
{
    BAJA_INTERFACE_NOTAG(iink_surface);

    virtual std::shared_ptr<model::iink_surface_model> model() = 0;

    virtual b::int32 addObserver(const std::shared_ptr<iink_surface_observer>& observer) = 0;
    virtual void removeObserver(const std::shared_ptr<iink_surface_observer>& observer) = 0;
    virtual void removeObserver(const b::int32 cookie) = 0;
    virtual void removeInkStroke(b::uint32 strokeId) = 0;
    virtual void selectTool(ink_surface_tool tool) = 0;
};

struct iink_surface_automation :
    public b::ibase
{
    BAJA_INTERFACE_NOTAG(iink_surface_automation);

    virtual void generateInk(const std::shared_ptr<model::iink_stroke_model>& newStroke) = 0;
};

struct iink_stroke : public b::ibase
{
    BAJA_INTERFACE_NOTAG(iink_stroke);

    virtual void render(const std::shared_ptr<model::iink_stroke_model>& model) = 0;
};

// observer interfaces

struct iink_surface_observer : public b::ibase
{
    BAJA_INTERFACE_NOTAG(iink_surface_observer);

    virtual void onStrokeAdded(const std::shared_ptr<model::iink_stroke_model>& newStroke) = 0;
    virtual void onDirty() = 0;
};

struct iphoto_observer : public b::ibase
{
    BAJA_INTERFACE_NOTAG(iphoto_observer);

    virtual void onPhotoMoved(
        const std::shared_ptr<view::iphoto>& photo,
        const b::float32& newScale,
        const m::point2d& newPosition,
        const b::float32& newAngle
        ) = 0;

    virtual void onPhotoSelected(
        const std::shared_ptr<view::iphoto>& photo
        ) = 0;

    virtual void onPhotoDeselected() = 0;

    virtual void onDirty() = 0;
};

struct ipage_observer : public iink_surface_observer, public iphoto_observer
{
    BAJA_INTERFACE_NOTAG(ipage_observer);

    virtual void onPhotoAdded(const std::shared_ptr<view::iphoto>& photo) = 0;
    virtual void onPhotoRemoved(const std::shared_ptr<view::iphoto>& photo) = 0;
    virtual void onThumbnailSaved(const std::shared_ptr<view::ipage>& page) = 0;
};

struct inotebook_observer : public ipage_observer
{
    BAJA_INTERFACE_NOTAG(inotebook_observer);
};

struct icamera_observer : public b::ibase
{
    BAJA_INTERFACE_NOTAG(icamera_observer);

    virtual void onCameraChange(const std::shared_ptr<icamera>& c) = 0;
};

struct inotebook_camera_driver : public b::ibase
{
    BAJA_INTERFACE_NOTAG(inotebook_camera_driver);

    virtual const std::shared_ptr<icamera> camera() = 0;
    virtual const std::shared_ptr<ipage_layout> layout() = 0;
};

struct inotebook_camera : public a::itick_observer
{
    BAJA_INTERFACE_NOTAG(inotebook_camera);

    virtual void moveToPage(
        const std::shared_ptr<ipage_layout>& layout, 
        const size_t pageNumber, 
        bool keepY, 
        const b::float32 duration = 0.f,
        const m::point2d& acceleration = m::point2d(0.1f, 0.1f)
        ) = 0;
    virtual void setAngles(
        const m::point3d& angles, 
        const b::float32 duration = 0.f,
        const m::point2d& acceleration = m::point2d(0.1f, 0.1f)
        ) = 0;
    virtual void setDistance(
        const b::float32 distance, 
        const b::float32 duration = 0.f,
        const m::point2d& acceleration = m::point2d(0.1f, 0.1f)
        ) = 0;
    virtual void setOffsetXY(
        const m::point2d& offsetXY, 
        const b::float32 duration = 0.f, 
        const m::point2d& acceleration = m::point2d(0.1f, 0.1f),
        const bool delta = false
        ) = 0;
    virtual void setScale(
        const b::float32 scale, 
        const b::float32 duration = 0.f,
        const m::point2d& acceleration = m::point2d(0.1f, 0.1f)
        ) = 0;
    virtual void reset(
        const std::shared_ptr<ipage_layout>& layout, 
        const b::float32 duration = 0.f,
        const m::point2d& acceleration = m::point2d(0.1f, 0.1f)
        ) = 0;
    virtual animation_3d& anglesAnimation() = 0;
    virtual animation& distanceAnimation() = 0;
    virtual animation& scaleAnimation() = 0;
    virtual animation_2d& offsetXYAnimation() = 0;

    virtual notebook_camera_state state() = 0;
};

}} // namespace journal::view
